from CvPythonExtensions import *

import PlanetfallSpells
from PlanetfallSpells import PlanetfallSpellImmediateBase
from PlanetfallSpells import PlanetfallSpellPlotTargetBase

import PlanetfallSpellsUtils as PSUtils

gc = CyGlobalContext()
import CvUtil

# Note: RIght now, these just translate straight into unit types. We might
# want to later on convert them to unit class types, and find out the actual
# unit class on the fly using the civilization's getCivilizationUnits function.

# This is a base class that is used for some of the other conversion classes.
class BaseConvertClass(PlanetfallSpellImmediateBase):
    def __init__(self):
        PlanetfallSpellImmediateBase.__init__(self)

        self.pCastingUnits = []
        self.pCastingUnits.append( gc.getInfoTypeForString("UNIT_UNITY_SUPPLIES") )

    def canUnitCast(self, pCastingUnit, pPlot, bVisible):
        if not pCastingUnit.getUnitType() in self.pCastingUnits:
            return False

        return True

    def startSpellResults(self, pCastingGroup, pTarget):
        iPlayer = pCastingGroup.getOwner()
        pPlayer = gc.getPlayer(iPlayer)
        pPlot = pCastingGroup.plot()

        for pUnit in pCastingGroup.getUnits():
            if self.canUnitCast(pUnit, pPlot, True):
                pNewUnit = pPlayer.initUnit(self.iConvertToUnit, pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
                pUnit.kill(True, PlayerTypes.NO_PLAYER)

class ConvertToRover(BaseConvertClass):
    def __init__(self):
        BaseConvertClass.__init__(self)

        # The unit it actually becomes.
        self.iConvertToUnit = gc.getInfoTypeForString("UNIT_SCOUT_ROVER")

    # The rest of the functions are already done in the base class.

class ConvertToFormer(BaseConvertClass):
    def __init__(self):
        BaseConvertClass.__init__(self)

        # The unit it actually becomes.
        self.iConvertToUnit = gc.getInfoTypeForString("UNIT_TERRAFORMER")

    # The rest of the functions are already done in the base class.


# Since a unit might need to select what water plot it should become a transport at,
# this spell will have to work a bit different.
class ConvertToTransport(PlanetfallSpellPlotTargetBase):
    def __init__(self):
        PlanetfallSpellPlotTargetBase.__init__(self)

        # The units that can cast this spell.
        self.pCastingUnits = []
        self.pCastingUnits.append( gc.getInfoTypeForString("UNIT_UNITY_SUPPLIES") )

        # The unit it actually becomes
        self.iConvertToUnit = gc.getInfoTypeForString("UNIT_TRANSPORT_FOIL")

        # The unit can only become a transport in adjacent plots, so it's size is one
        self.CONVERT_CAST_RANGE = 1

    # A little function that is used in a few places to help determine if a plot
    # is a reasonable spot for a unit to become a transport on. pCastingUnitOrGroup
    # can be a CyUnit or CyGroup object.
    def canUnitOrGroupBecomeTransportInPlot(self, pCastingUnitOrGroup, pCastingPlot, pPlot):

        # Check if we can enter the plot based on who owns it.
        if not pCastingUnitOrGroup.canEnterTerritory(pPlot.getTeam(), False):
            return False
            
        # Check if we can enter the plot based on it being water (or being a city near water)
        if pPlot.isWater():
            if pPlot.isLake():
                return False

        else:
            # The only land plot we can create on is one where there is a city next to an ocean plot.
            bValid = False
            if pPlot.isCity():
                if pPlot.isOceanCoastalPlot():
                    bValid = True

            if not bValid:
                return False

        # Make sure that plot is within one plot's distance.
        if plotDistance(pCastingPlot.getX(), pCastingPlot.getY(), pPlot.getX(), pPlot.getY()) > self.CONVERT_CAST_RANGE:
            return False

        # Make sure that there are no visible enemy units in the plot:
        for i in range(0, pCastingPlot.getNumUnits()):
            pUnit = pCastingPlot.getUnit(i)
            if not pUnit.isInvisible(pCastingUnitOrGroup.getTeam(), False):
                if gc.getTeam(pUnit.getTeam()).isAtWar(pCastingUnitOrGroup.getTeam()):
                    return False

        return True


    def canUnitCast(self, pCastingUnit, pCastingPlot, bVisible):
        if not pCastingUnit.getUnitType() in self.pCastingUnits:
            return False

        if bVisible:
            return True

        # There should be at least ONE spot where the unit can cast.
        iCastingX = pCastingPlot.getX()
        iCastingY = pCastingPlot.getY()
        bFoundAPlot = False
        for iX in range(iCastingX - 1, iCastingX + 2):
            for iY in range(iCastingY - 1, iCastingY + 2):
                if self.canUnitOrGroupBecomeTransportInPlot(pCastingUnit, pCastingPlot, gc.getMap().plot(iX, iY)):
                    bFoundAPlot = True
                    break

        if not bFoundAPlot:
            return False
        
        return True

    def getRangePlots(self, pCastingGroup, pCastingPlot, pHoverPlot):
        # Get a list of all the plots in the set distance that we can cast to.
        pRangePlots = PSUtils.getRangePlotDistanceList(pCastingPlot, self.CONVERT_CAST_RANGE + 1)

        return pRangePlots

    def canTargetPlot(self, pCastingGroup, pCastingPlot, pPotentialPlot):
        # Only returns the plots that we can target
        for pUnit in pCastingGroup.getUnits():
            if self.canUnitOrGroupBecomeTransportInPlot(pUnit, pCastingPlot, pPotentialPlot):
                return True

        return False

    def startSpellResultsOnPlot(self, pCastingGroup, pPlot):
        pCastingPlayer = gc.getPlayer(pCastingGroup.getOwner())
        pCastingPlot = pCastingGroup.plot()
        for pCastingUnit in pCastingGroup.getUnits():
            if self.canUnitCast(pCastingUnit, pCastingPlot, pPlot):
                pNewUnit = pCastingPlayer.initUnit(self.iConvertToUnit, pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
                pCastingUnit.kill(True, PlayerTypes.NO_PLAYER)
